package com.gylang.print.pdf.render;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.gylang.print.pdf.domain.PdfPageWrapper;
import com.gylang.print.pdf.domain.PdfWrapper;
import com.gylang.print.pdf.domain.component.table.PdfTable;
import com.gylang.print.pdf.domain.component.table.PdfTableField;
import com.gylang.print.pdf.domain.component.table.PdfTableFieldWrapper;
import com.gylang.print.pdf.domain.component.table.TableComponent;
import com.gylang.print.pdf.domain.component.text.TextPdfComponent;
import com.gylang.print.pdf.enums.AlignEnums;
import com.gylang.print.pdf.enums.PdfComponentEnums;
import com.gylang.print.pdf.util.PdfUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.font.PDFont;

import java.awt.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author gylang
 * data 2021/6/7
 */
@Slf4j
public class TableRender implements IPdfRender<TableComponent> {


	private static final Pattern BEAN_FORMAT = Pattern.compile("\\{\\w+}");

	@Override
	public String type() {
		return PdfComponentEnums.TABLE.getType();
	}

	@Override
	public void render(PdfWrapper wrapper, PdfPageWrapper pageWrapper, TableComponent component) {

		// 获取表格数据
		Map<String, PdfTableFieldWrapper> tableFieldMap = new HashMap<>();
		List<Map<String, Object>> dataMap = resolveArgument(wrapper, component, tableFieldMap);

		// 列信息
		List<Map.Entry<String, PdfTableFieldWrapper>> headerList = tableFieldMap.entrySet().stream()
			.sorted(Comparator.comparingInt(f -> f.getValue().getOrder())).collect(Collectors.toList());


		// 数据的格式转换
		converter(tableFieldMap, dataMap);

		// 获取每行表格宽度
		List<Float> heightList = new ArrayList<>();
		float totalHeight = 0F;

		// 获取每列宽度
		List<Float> widthList = new ArrayList<>();
		float totalWidth = 0F;
		for (Map.Entry<String, PdfTableFieldWrapper> header : headerList) {

			// 获取当前列的长宽
			float width = getWidth(component, header, dataMap);
			widthList.add(width);
			totalWidth += width;
		}

		// 获取每行高度
		for (Map<String, Object> map : dataMap) {

			float height = getHeightAndSetLines(tableFieldMap, map);
			totalHeight += height;
			heightList.add(height);
		}



		// 渲染数据
		try {
			renderTable(wrapper, pageWrapper, headerList, dataMap, component, totalWidth, totalHeight, widthList, heightList);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 渲染表格数据
	 *
	 * @param wrapper     doc信息
	 * @param pageWrapper 页面信息
	 * @param headerList  表头列表 (有序)
	 * @param dataMap     数据列表
	 * @param component   表格组件信息
	 * @param totalWidth  单元格总宽度
	 * @param totalHeight 单元格总高度
	 * @param widthList   单元格宽度列表
	 * @param heightList  单元格长度列表
	 */
	private void renderTable(PdfWrapper wrapper, PdfPageWrapper pageWrapper,
							 List<Map.Entry<String, PdfTableFieldWrapper>> headerList,
							 List<Map<String, Object>> dataMap, TableComponent component,
							 float totalWidth, float totalHeight, List<Float> widthList, List<Float> heightList) throws IOException {


		// 总高度  = 表格数据总高度 + 表头高度 + 边框总高度 * 边框数
		float actualHeight = totalHeight + component.getBorder() * (heightList.size() + 1);
		// 总宽度  = 表格数据总宽度 + 边框总宽度 * 边框数
		float actualWidth = totalWidth + component.getBorder() * (widthList.size() + 1);
		// 渲染表格背景
		Color borderColor = component.getBorderColor();
		float y = PdfUtil.getY(pageWrapper.getHeight(), pageWrapper.getMarginTopY(), pageWrapper.getMarginBottomY(),
			component.getY(), actualHeight, component.getAlign());
		float x = PdfUtil.getX(pageWrapper.getWidth(), pageWrapper.getMarginLeftX(), pageWrapper.getMarginRightX(),
			component.getX(), actualWidth, component.getAlign());
		// 背景图
		PdfUtil.writeRectangle(pageWrapper.getContentStream(), borderColor, x, y, actualWidth, actualHeight);

		// 渲染每个表格

		// .. 表头
		int offsetY = 0;

		float tempX = x + component.getBorder();
		float tempY = y + actualHeight - component.getBorder() - heightList.get(offsetY);
		write(wrapper, pageWrapper, headerList, component, widthList, heightList, tempX, tempY, offsetY, dataMap.get(0));

		// .. 数据表格
		offsetY += 1;
		for (int i = offsetY; i < dataMap.size(); i++) {
			tempY -= heightList.get(offsetY) + component.getBorder();
			tempX = x + component.getBorder();
			write(wrapper, pageWrapper, headerList, component, widthList, heightList, tempX, tempY, offsetY, dataMap.get(i));
			offsetY++;
		}


	}

	private void write(PdfWrapper wrapper, PdfPageWrapper pageWrapper, List<Map.Entry<String, PdfTableFieldWrapper>> headerList,
					   TableComponent component, List<Float> widthList, List<Float> heightList,
					   float tempX, float tempY, int offsetY, Map<String, Object> map) {
		int offsetX = 0;
		for (Map.Entry<String, PdfTableFieldWrapper> entry : headerList) {
			PdfTableFieldWrapper value = entry.getValue();

			PdfUtil.writeRectangle(pageWrapper.getContentStream(), entry.getValue().getBgColor(), tempX, tempY, widthList.get(offsetX), heightList.get(offsetY));
			writerText(component, wrapper, pageWrapper, tempX, tempY, value, (List<String>) map.get(entry.getKey()));
			// 移动 x轴
			tempX += widthList.get(offsetX) + component.getBorder();
			offsetX++;
		}
	}


	/**
	 * 写入单元格文本
	 *
	 * @param component
	 * @param wrapper     pdf数据
	 * @param pageWrapper 页面数据
	 * @param x           x
	 * @param y           y
	 * @param cell        单元信息
	 * @param content     内容
	 */
	private void writerText(TableComponent component, PdfWrapper wrapper, PdfPageWrapper pageWrapper, float x, float y, PdfTableFieldWrapper cell, List<String> content) {
		TextPdfComponent textPdfComponent = new TextPdfComponent(pageWrapper)
			// 宽度为 -> 文本宽度, 高度为文本高度
			.funBuilder(t -> t.setMultiLine(content))
			.funBuilder(t -> t.setBr(true))
			.funBuilder(t -> t.setBrInterval(cell.getBrInterval()))
			.funBuilder(t -> t.setSize(cell.getFontSize()))
			.funBuilder(t -> t.setColor(cell.getTextColor()))
			.funBuilder(t -> t.autoSize(AlignEnums.ABSOLUTE, x + cell.getMarginLeftX(), y + cell.getMarginTopY() + cell.getBrInterval() + +component.getBorder()));
		PdfRenderHelper.renderByType(wrapper, pageWrapper, textPdfComponent);
	}


	/**
	 * 数据转换
	 *
	 * @param cellMap 单元格信息
	 * @param dataMap 数据
	 */
	private void converter(Map<String, PdfTableFieldWrapper> cellMap, List<Map<String, Object>> dataMap) {

		for (Map<String, Object> map : dataMap) {
			for (Map.Entry<String, Object> entry : map.entrySet()) {
				String key = entry.getKey();
				Object value = entry.getValue();
				PdfTableFieldWrapper pdfTableFieldWrapper = cellMap.get(key);
				String dateFormat = pdfTableFieldWrapper.getDateFormat();
				String readConverterExp = pdfTableFieldWrapper.getReadConverterExp();
				String format = pdfTableFieldWrapper.getFormat();
				if (StrUtil.isNotEmpty(readConverterExp)) {
					value = reverseByExp(value.toString(), readConverterExp);
				}
				if (StrUtil.isNotEmpty(dateFormat)) {
					value = DateUtil.format(Convert.convert(Date.class, value), dateFormat);
				}
				if (StrUtil.isNotEmpty(format)) {
					value = format(value, format, map);
				}
				map.put(key, value);
			}
		}

	}


	/**
	 * 格式化
	 *
	 * @param value  数值
	 * @param format 模板
	 * @param vo     vo对象
	 * @return
	 */
	private String format(Object value, String format, Object vo) {
		if (BEAN_FORMAT.matcher(format).find()) {
			return StrUtil.format(format, BeanUtil.beanToMap(vo));
		} else {
			return StrUtil.format(format, value);
		}
	}

	/**
	 * 读取内容转表达式 (如: 0=男,1=女,2=未知)
	 *
	 * @param propertyValue 数值
	 * @param converterExp  模板
	 * @return
	 */
	public static String reverseByExp(String propertyValue, String converterExp) {
		StringBuilder propertyString = new StringBuilder();
		String[] convertSource = converterExp.split(",");
		for (String item : convertSource) {
			String[] itemArray = item.split("=");

			if (itemArray[1].equals(propertyValue)) {
				return itemArray[0];
			}
		}
		return StrUtil.strip(propertyString.toString(), null, ",");
	}


	/**
	 * 切割数据 和 获取高度
	 *
	 * @param header  单元格信息
	 * @param dataMap 数据
	 * @return
	 */
	private float getHeightAndSetLines(Map<String, PdfTableFieldWrapper> header, Map<String, Object> dataMap) {

		// 遍历当前行的所有列 获取最大高度
		float maxHeight = 0F;
		float temp;
		for (Map.Entry<String, Object> entry : dataMap.entrySet()) {

			Object value = entry.getValue();
			String key = entry.getKey();
			PdfTableFieldWrapper fieldWrapper = header.get(key);
			if (PdfTableField.Flex.HEIGHT_FLEX.equals(fieldWrapper.getFlex())) {
				// 计算当前高度
				List<String> lines = PdfUtil.splitLines(value.toString(), fieldWrapper.getWidth() - fieldWrapper.getMarginRightX() - fieldWrapper.getMarginLeftX(),
					fieldWrapper.getFont(), fieldWrapper.getFontSize());
				dataMap.put(key, lines);
				temp = fieldWrapper.getMarginBottomY() + fieldWrapper.getMarginTopY() + (fieldWrapper.getFontSize() + fieldWrapper.getBrInterval()) * lines.size();
			} else {
				temp = fieldWrapper.getHeight();
				dataMap.put(key, CollUtil.newArrayList(value.toString()));
			}
			maxHeight = Float.max(maxHeight, temp);
		}
		return maxHeight;
	}

	/**
	 * 获取宽度
	 *
	 * @param component 组件信息
	 * @param header    表格信息
	 * @param dataMap   数据
	 * @return
	 */
	private float getWidth(TableComponent component, Map.Entry<String, PdfTableFieldWrapper> header, List<Map<String, Object>> dataMap) {

		PdfTableFieldWrapper fieldWrapper = header.getValue();
		String key = header.getKey();
		PdfTableField.Flex flex = fieldWrapper.getFlex();
		if (PdfTableField.Flex.WITH_FLEX.equals(flex)) {
			// 可变宽度 计算当前列中最大宽度
			// 计算宽度
			PDFont font = fieldWrapper.getFont();
			return fieldWrapper.getMarginLeftX() + fieldWrapper.getMarginRightX() + dataMap.stream()
				.filter(d -> null != d.get(key))
				.map(d -> PdfUtil.getTextLength(font, fieldWrapper.getFontSize(), d.get(key).toString()))
				.max(Float::compareTo)
				// 按照表头的长度展示
				.orElse(PdfUtil.getTextLength(font, component.getHeaderFontSize(), fieldWrapper.getName()));

		} else {
			return fieldWrapper.getWidth();
		}
	}

	/**
	 * 参数解析
	 *
	 * @param wrapper   pdf
	 * @param component 组件数据
	 * @param cellMap   单元格信息
	 * @return
	 */
	private List<Map<String, Object>> resolveArgument(PdfWrapper wrapper, TableComponent component, Map<String, PdfTableFieldWrapper> cellMap) {
		List<Map<String, Object>> dataMap = component.getDataMap();
		PDFont defaultFont = null;
		if (null == dataMap) {
			dataMap = new ArrayList<>();
			// 尝试从bean中获取dataMap
			List<Object> mappingBean = component.getMappingBean();
			if (CollUtil.isEmpty(mappingBean)) {
				log.error("[表格渲染]: 数据为空");
				throw new ValidateException("表格数据为空");
			}

			// 获取表格注解
			PdfTable pdfTable = AnnotationUtil.getAnnotation(mappingBean.get(0).getClass(), PdfTable.class);
			if (null != pdfTable) {
				component.setBorderColor(new Color(pdfTable.borderColor()));
				component.setFont(PdfUtil.loadFontByName(wrapper.getDocument(), pdfTable.font()));
				component.setHeaderBgColor(new Color(pdfTable.headerBgColor()));
				component.setHeaderFontSize(pdfTable.headerFontSize());
				component.setHeaderTextColor(new Color(pdfTable.headerTextColor()));
				component.setTitle(pdfTable.title());
			}
			defaultFont = null == component.getFont() ? PdfUtil.loadFontByName(wrapper.getDocument(), component.getFontName()) : component.getFont();
			component.setFont(defaultFont);

			Field[] fields = ReflectUtil.getFields(mappingBean.get(0).getClass());

			// 获取存在注解的fields
			for (Field field : fields) {
				PdfTableField annotation = AnnotationUtil.getAnnotation(field, PdfTableField.class);
				if (null != annotation) {
					PdfTableFieldWrapper pdfTableFieldWrapper = new PdfTableFieldWrapper();
					pdfTableFieldWrapper.setFont(StrUtil.isEmpty(annotation.font()) ? defaultFont : PdfUtil.loadFontByName(wrapper.getDocument(), annotation.font()));
					pdfTableFieldWrapper.setFontSize(annotation.fontSize());
					pdfTableFieldWrapper.setName(StrUtil.isEmpty(annotation.name()) ? field.getName() : annotation.name());
					pdfTableFieldWrapper.setBgColor(new Color(annotation.bgColor()));
					pdfTableFieldWrapper.setTextColor(new Color(annotation.textColor()));
					pdfTableFieldWrapper.setWidth(annotation.width());
					pdfTableFieldWrapper.setHeight(annotation.height());
					pdfTableFieldWrapper.setFlex(annotation.flex());
					pdfTableFieldWrapper.setInterval(annotation.interval());
					pdfTableFieldWrapper.setBrInterval(annotation.brInterval());
					pdfTableFieldWrapper.setMarginLeftX(annotation.marginLeftX());
					pdfTableFieldWrapper.setMarginRightX(annotation.marginRightX());
					pdfTableFieldWrapper.setMarginTopY(annotation.marginTopY());
					pdfTableFieldWrapper.setMarginBottomY(annotation.marginBottomY());
					cellMap.put(field.getName(), pdfTableFieldWrapper);
				}
			}
			//生成mapData
			Map<String, Object> headerData = cellMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, kv -> kv.getValue().getName()));
			dataMap.add(headerData);
			for (Object o : mappingBean) {

				Map<String, Object> map = BeanUtil.beanToMap(o, CollUtil.newHashMap(cellMap.size()), false, k -> cellMap.containsKey(k) ? k : null);
				dataMap.add(map);
			}

		} else {
			// 获取表头信息
			defaultFont = null == component.getFont() ? PdfUtil.loadFontByName(wrapper.getDocument(), component.getFontName()) : component.getFont();
			component.setFont(defaultFont);
			Set<String> keySet = dataMap.get(0).keySet();
			PdfTableFieldWrapper defaultField = new PdfTableFieldWrapper();
			defaultField.setFont(defaultFont);
			defaultField.setTextColor(new Color(0x000000));
			defaultField.setBgColor(new Color(0xFFFFFF));
			for (String key : keySet) {
				PdfTableFieldWrapper pdfTableFieldWrapper = cellMap.get(key);
				pdfTableFieldWrapper = null == pdfTableFieldWrapper ? defaultField : pdfTableFieldWrapper;
				cellMap.put(key, pdfTableFieldWrapper);
			}
		}
		return dataMap;
	}
}
